home *** CD-ROM | disk | FTP | other *** search
- Frequently Asked Questions (FAQS);faqs.538
-
-
-
- Some vendors provide a "/proc" filesystem, which appears as a
- directory with a bunch of filenames in it. Each filename is a
- number, corresponding to a process ID, and you can open the file
- and read it to get information about the process. Once again,
- access to this may be restricted, and the interface to it may
- change from system to system.
-
- If you can't use vendor-specific library functions, and you
- don't have /proc, and you still want to do this completely
- in C, you
- are going to have to do the grovelling through kernel memory
- yourself. For a good example of how to do this on many systems,
- see the sources to "ofiles", available in the comp.sources.unix
- archives. (A package named "kstuff" to help with kernel
- grovelling was posted to alt.sources in May 1991 and is also
- available via anonymous ftp as
- usenet/alt.sources/articles/{329{6,7,8,9},330{0,1}}.Z from
- wuarchive.wustl.edu.)
-
- 3.11) How do I check the exit status of a remote command
- executed via "rsh" ?
-
- This doesn't work:
-
- rsh some-machine some-crummy-command || echo "Command failed"
-
- The exit status of 'rsh' is 0 (success) if the rsh program
- itself completed successfully, which probably isn't what
- you wanted.
-
- If you want to check on the exit status of the remote program,
- you can try using Maarten Litmaath's 'ersh' script, which was
- posted to alt.sources in January, 1991. ersh is a shell script
- that calls rsh, arranges for the remote machine to echo the
- status of the command after it completes, and exits with that
- status.
-
- 3.12) Is it possible to pass shell variable settings into an awk program?
-
- There are two different ways to do this. The first involves
- simply expanding the variable where it is needed in the program.
- For example, to get a list of all ttys you're using:
-
- who | awk '/^'"$USER"'/ { print $2 }' (1)
-
- Single quotes are usually used to enclose awk programs because
- the character '$' is often used in them, and '$' will be
- interpreted by the shell if enclosed inside double quotes, but
- not if enclosed inside single quotes. In this case, we *want*
- the '$' in "$USER" to be interpreted by the shell, so we close
- the single quotes and then put the "$USER" inside double quotes.
- Note that there are no spaces in any of that, so the shell will
- see it all as one argument. Note, further, that the double
- quotes probably aren't necessary in this particular case (i.e. we
- could have done
-
- who | awk '/^'$USER'/ { print $2 }' (2)
-
- ), but they should be included nevertheless because they are
- necessary when the shell variable in question contains special
- characters or spaces.
-
- The second way to pass variable settings into awk is to use an
- often undocumented feature of awk which allows variable settings
- to be specified as "fake file names" on the command line. For
- example:
-
- who | awk '$1 == user { print $2 }' user="$USER" - (3)
-
- Variable settings take effect when they are encountered on the
- command line, so, for example, you could instruct awk on how to
- behave for different files using this technique. For example:
-
- awk '{ program that depends on s }' s=1 file1 s=0 file2 (4)
-
- Note that some versions of awk will cause variable settings
- encountered before any real filenames to take effect before the
- BEGIN block is executed, but some won't so neither way should be
- relied upon.
-
- Note, further, that when you specify a variable setting, awk
- won't automatically read from stdin if no real files are
- specified, so you need to add a "-" argument to the end of your
- command, as I did at (3) above.
-
- 3.13) How do I get rid of zombie processes that persevere?
-
- From: jik@pit-manager.MIT.Edu (Jonathan I. Kamens)
- Date: Fri, 17 Jan 92 14:40:09 -0500
-
- Unfortunately, it's impossible to generalize how the death of
- child processes should behave, because the exact mechanism varies
- over the various flavors of Unix.
-
- First of all, by default, you have to do a wait() for child
- processes under ALL flavors of Unix. That is, there is no flavor
- of Unix that I know of that will automatically flush child
- processes that exit, even if you don't do anything to tell it to
- do so.
-
- Second, under some SysV-derived systems, if you do
- "signal(SIGCHLD, SIG_IGN)" (well, actually, it may be SIGCLD
- instead of SIGCHLD, but most of the newer SysV systems have
- "#define SIGCHLD SIGCLD" in the header files), then child
- processes will be cleaned up automatically, with no further
- effort in your part. The best way to find out if it works at
- your site is to try it, although if you are trying to write
- portable code, it's a bad idea to rely on this in any case.
- Unfortunately, POSIX doesn't allow you to do this; the behavior
- of setting the SIGCHLD to SIG_IGN under POSIX is undefined, so
- you can't do it if your program is supposed to be
- POSIX-compliant.
-
- If you can't use SIG_IGN to force automatic clean-up, then you've
- got to write a signal handler to do it. It isn't easy at all to
- write a signal handler that does things right on all flavors of
- Unix, because of the following inconsistencies:
-
- On some flavors of Unix, the SIGCHLD signal handler is called if
- one *or more* children have died. This means that if your signal
- handler only does one wait() call, then it won't clean up all of
- the children. Fortunately, I believe that all Unix flavors for
- which this is the case have available to the programmer the
- wait3() call, which allows the WNOHANG option to check whether or
- not there are any children waiting to be cleaned up. Therefore,
- on any system that has wait3(), your signal handler should call
- wait3() over and over again with the WNOHANG option until there
- are no children left to clean up.
-
- On SysV-derived systems, SIGCHLD signals are regenerated if there
- are child processes still waiting to be cleaned up after you exit
- the SIGCHLD signal handler. Therefore, it's safe on most SysV
- systems to assume when the signal handler gets called that you
- only have to clean up one signal, and assume that the handler
- will get called again if there are more to clean up after it
- exits.
-
- On older systems, signal handlers are automatically reset to
- SIG_DFL when the signal handler gets called. On such systems,
- you have to put "signal(SIGCHILD, catcher_func)" (where
- "catcher_func" is the name of the handler function) as the first
- thing in the signal handler, so that it gets reset.
- Unfortunately, there is a race condition which may cause you to
- get a SIGCHLD signal and have it ignored between the time your
- handler gets called and the time you reset the signal.
- Fortunately, newer implementations of signal() don't reset the
- handler to SIG_DFL when the handler function is called. To get
- around this problem, on systems that do not have wait3() but do
- have SIGCLD, you need to reset the signal handler with a call to
- signal() after doing at least one wait() within the handler, each
- time it is called.
-
- The summary of all this is that on systems that have wait3(), you
- should use that and your signal handler should loop, and on
- systems that don't, you should have one call to wait() per
- invocation of the signal handler.
-
- One more thing -- if you don't want to go through all of this
- trouble, there is a portable way to avoid this problem, although
- it is somewhat less efficient. Your parent process should fork,
- and then wait right there and then for the child process to
- terminate. The child process then forks again, giving you a
- child and a grandchild. The child exits immediately (and hence
- the parent waiting for it notices its death and continues to
- work), and the grandchild does whatever the child was originally
- supposed to. Since its parent died, it is inherited by init,
- which will do whatever waiting is needed. This method is
- inefficient because it requires an extra fork, but is pretty much
- completely portable.
-
- 3.14) How do I get lines from a pipe as they are written instead of only in
- larger blocks.
-
- From: jik@pit-manager.MIT.Edu (Jonathan I. Kamens)
- Date: Sun, 16 Feb 92 20:59:28 -0500
-
- The stdio library does buffering differently depending on whether
- it thinks it's running on a tty. If it thinks it's on a tty, it
- does buffering on a per-line basis; if not, it uses a larger
- buffer than one line.
-
- If you have the source code to the client whose buffering you
- want to disable, you can use setbuf() or setvbuf() to change the
- buffering.
-
- If not, the best you can do is try to convince the program that
- it's running on a tty by running it under a pty, e.g. by using
- the "pty" program mentioned in question 3.9.
-
- --
- Ted Timar - tmatimar@empress.com
- Empress Software, 3100 Steeles Ave E, Markham, Ont., Canada L3R 8T3
- Xref: bloom-picayune.mit.edu comp.unix.questions:51336 comp.unix.shell:8342 news.answers:4778
- Path: bloom-picayune.mit.edu!senator-bedfellow.mit.edu!senator-bedfellow.mit.edu!usenet
- From: tmatimar@empress.com (Ted M A Timar)
- Newsgroups: comp.unix.questions,comp.unix.shell,news.answers
- Subject: Unix - Frequently Asked Questions (4/7) [Frequent posting]
- Supersedes: <unix-faq/faq/part4_723967331@athena.mit.edu>
- Followup-To: comp.unix.questions
- Date: 24 Dec 1992 06:03:15 GMT
- Organization: Empress Software
- Lines: 555
- Approved: news-answers-request@MIT.Edu
- Distribution: world
- Expires: 21 Jan 1993 06:02:09 GMT
- Message-ID: <unix-faq/faq/part4_725176929@athena.mit.edu>
- References: <unix-faq/faq/contents_725176929@athena.mit.edu>
- NNTP-Posting-Host: pit-manager.mit.edu
- X-Last-Updated: 1992/12/09
-
- Archive-name: unix-faq/faq/part4
- Version: $Id: part4,v 2.1 92/12/04 07:43:53 tmatimar Exp $
-
- These seven articles contain the answers to some Frequently Asked
- Questions often seen in comp.unix.questions and comp.unix.shell.
- Please don't ask these questions again, they've been answered plenty
- of times already - and please don't flame someone just because they may
- not have read this particular posting. Thank you.
-
- These articles are divided approximately as follows:
-
- 1.*) General questions.
- 2.*) Relatively basic questions, likely to be asked by beginners.
- 3.*) Intermediate questions.
- 4.*) Advanced questions, likely to be asked by people who thought
- they already knew all of the answers.
- 5.*) Questions pertaining to the various shells, and the differences.
- 6.*) An overview of Unix variants.
- 7.*) An comparison of configuration management systems (RCS, SCCS).
-
- This article includes answers to:
-
- 4.1) How do I read characters from a terminal without requiring the user
- to hit RETURN?
- 4.2) How do I check to see if there are characters to be read without
- actually reading?
- 4.3) How do I find the name of an open file?
- 4.4) How can an executing program determine its own pathname?
- 4.5) How do I use popen() to open a process for reading AND writing?
- 4.6) How do I sleep() in a C program for less than one second?
- 4.7) How can I get setuid shell scripts to work?
- 4.8) How can I find out which user or process has a file open or is using
- a particular file system (so that I can unmount it?)
- 4.9) How do I keep track of people who are fingering me?
- 4.10) Is it possible to reconnect a process to a terminal after it has
- been disconnected, e.g. after starting a program in the background
- and logging out?
- 4.11) Is it possible to "spy" on a terminal, displaying the output
- that's appearing on it on another terminal?
-
- If you're looking for the answer to, say, question 4.5, and want to skip
- everything else, you can search ahead for the regular expression "^4.5)".
-
- While these are all legitimate questions, they seem to crop up in
- comp.unix.questions or comp.unix.shell on an annual basis, usually
- followed by plenty of replies (only some of which are correct) and then
- a period of griping about how the same questions keep coming up. You
- may also like to read the monthly article "Answers to Frequently Asked
- Questions" in the newsgroup "news.announce.newusers", which will tell
- you what "UNIX" stands for.
-
- With the variety of Unix systems in the world, it's hard to guarantee
- that these answers will work everywhere. Read your local manual pages
- before trying anything suggested here. If you have suggestions or
- corrections for any of these answers, please send them to to
- tmatimar@empress.com.
-
- 4.1) How do I read characters from a terminal without requiring the user
- to hit RETURN?
-
- Check out cbreak mode in BSD, ~ICANON mode in SysV.
-
- If you don't want to tackle setting the terminal parameters
- yourself (using the "ioctl(2)" system call) you can let the stty
- program do the work - but this is slow and inefficient, and you
- should change the code to do it right some time:
-
- #include <stdio.h>
- main()
- {
- int c;
-
- printf("Hit any character to continue\n");
- /*
- * ioctl() would be better here; only lazy
- * programmers do it this way:
- */
- system("/bin/stty cbreak"); /* or "stty raw" */
- c = getchar();
- system("/bin/stty -cbreak");
- printf("Thank you for typing %c.\n", c);
-
- exit(0);
- }
-
- You might like to check out the documentation for the "curses"
- library of portable screen functions. Often if you're interested
- in single-character I/O like this, you're also interested in
- doing some sort of screen display control, and the curses library
- provides various portable routines for both functions.
-
- 4.2) How do I check to see if there are characters to be read without
- actually reading?
-
- Certain versions of UNIX provide ways to check whether characters
- are currently available to be read from a file descriptor. In
- BSD, you can use select(2). You can also use the FIONREAD ioctl
- (see tty(4)), which returns the number of characters waiting to
- be read, but only works on terminals, pipes and sockets. In
- System V Release 3, you can use poll(2), but that only works on
- streams. In Xenix - and therefore Unix SysV r3.2 and later - the
- rdchk() system call reports whether a read() call on a given file
- descriptor will block.
-
- There is no way to check whether characters are available to be
- read from a FILE pointer. (You could poke around inside stdio
- data structures to see if the input buffer is nonempty, but that
- wouldn't work since you'd have no way of knowing what will happen
- the next time you try to fill the buffer.)
-
- Sometimes people ask this question with the intention of writing
- if (characters available from fd)
- read(fd, buf, sizeof buf);
- in order to get the effect of a nonblocking read. This is not
- the best way to do this, because it is possible that characters
- will be available when you test for availability, but will no
- longer be available when you call read. Instead, set the
- O_NDELAY flag (which is also called FNDELAY under BSD) using the
- F_SETFL option of fcntl(2). Older systems (Version 7, 4.1 BSD)
- don't have O_NDELAY; on these systems the closest you can get to
- a nonblocking read is to use alarm(2) to time out the read.
-
- 4.3) How do I find the name of an open file?
-
- In general, this is too difficult. The file descriptor may
- be attached to a pipe or pty, in which case it has no name.
- It may be attached to a file that has been removed. It may
- have multiple names, due to either hard or symbolic links.
-
- If you really need to do this, and be sure you think long
- and hard about it and have decided that you have no choice,
- you can use find with the -inum and possibly -xdev option,
- or you can use ncheck, or you can recreate the functionality
- of one of these within your program. Just realize that
- searching a 600 megabyte filesystem for a file that may not
- even exist is going to take some time.
-
- 4.4) How can an executing program determine its own pathname?
-
- Your program can look at argv[0]; if it begins with a "/", it is
- probably the absolute pathname to your program, otherwise your
- program can look at every directory named in the environment
- variable PATH and try to find the first one that contains an
- executable file whose name matches your program's argv[0] (which
- by convention is the name of the file being executed). By
- concatenating that directory and the value of argv[0] you'd
- probably have the right name.
-
- You can't really be sure though, since it is quite legal for one
- program to exec() another with any value of argv[0] it desires.
- It is merely a convention that new programs are exec'd with the
- executable file name in argv[0].
-
- For instance, purely a hypothetical example:
-
- #include <stdio.h>
- main()
- {
- execl("/usr/games/rogue", "vi Thesis", (char *)NULL);
- }
-
- The executed program thinks its name (its argv[0] value) is
- "vi Thesis". (Certain other programs might also think that
- the name of the program you're currently running is "vi Thesis",
- but of course this is just a hypothetical example, don't
- try it yourself :-)
-
- 4.5) How do I use popen() to open a process for reading AND writing?
-
- The problem with trying to pipe both input and output to an
- arbitrary slave process is that deadlock can occur, if both
- processes are waiting for not-yet-generated input at the same
- time. Deadlock can be avoided only by having BOTH sides follow a
- strict deadlock-free protocol, but since that requires
- cooperation from the processes it is inappropriate for a
- popen()-like library function.
-
- The 'expect' distribution includes a library of functions that a
- C programmer can call directly. One of the functions does the
- equivalent of a popen for both reading and writing. It uses ptys
- rather than pipes, and has no deadlock problem. It's portable to
- both BSD and SV. See the next answer for more about 'expect'.
-
- 4.6) How do I sleep() in a C program for less than one second?
-
- The first thing you need to be aware of is that all you can
- specify is a MINIMUM amount of delay; the actual delay will
- depend on scheduling issues such as system load, and could be
- arbitrarily large if you're unlucky.
-
- There is no standard library function that you can count on in
- all environments for "napping" (the usual name for short
- sleeps). Some environments supply a "usleep(n)" function which
- suspends execution for n microseconds. If your environment
- doesn't support usleep(), here are a couple of implementations
- for BSD and System V environments.
-
- The following code is adapted from Doug Gwyn's System V emulation
- support for 4BSD and exploits the 4BSD select() system call.
- Doug originally called it 'nap()'; you probably want to call it
- "usleep()";
-
- /*
- usleep -- support routine for 4.2BSD system call emulations
- last edit: 29-Oct-1984 D A Gwyn
- */
-
- extern int select();
-
- int
- usleep( usec ) /* returns 0 if ok, else -1 */
- long usec; /* delay in microseconds */
- {
- static struct /* `timeval' */
- {
- long tv_sec; /* seconds */
- long tv_usec; /* microsecs */
- } delay; /* _select() timeout */
-
- delay.tv_sec = usec / 1000000L;
- delay.tv_usec = usec % 1000000L;
-
- return select( 0, (long *)0, (long *)0, (long *)0, &delay );
- }
-
- On System V you might do it this way:
-
- /*
- subseconds sleeps for System V - or anything that has poll()
- Don Libes, 4/1/1991
-
- The BSD analog to this function is defined in terms of
- microseconds while poll() is defined in terms of milliseconds.
- For compatibility, this function provides accuracy "over the long
- run" by truncating actual requests to milliseconds and
- accumulating microseconds across calls with the idea that you are
- probably calling it in a tight loop, and that over the long run,
- the error will even out.
-
- If you aren't calling it in a tight loop, then you almost
- certainly aren't making microsecond-resolution requests anyway,
- in which case you don't care about microseconds. And if you did,
- you wouldn't be using UNIX anyway because random system
- indigestion (i.e., scheduling) can make mincemeat out of any
- timing code.
-
- Returns 0 if successful timeout, -1 if unsuccessful.
-
- */
-
- #include <poll.h>
-
- int
- usleep(usec)
- unsigned int usec; /* microseconds */
- {
- static subtotal = 0; /* microseconds */
- int msec; /* milliseconds */
-
- /* 'foo' is only here because some versions of 5.3 have
- * a bug where the first argument to poll() is checked
- * for a valid memory address even if the second argument is 0.
- */
- struct pollfd foo;
-
- subtotal += usec;
- /* if less then 1 msec request, do nothing but remember it */
- if (subtotal < 1000) return(0);
- msec = subtotal/1000;
- subtotal = subtotal%1000;
- return poll(&foo,(unsigned long)0,msec);
- }
-
- Another possibility for nap()ing on System V, and probably other
- non-BSD Unices is Jon Zeeff's s5nap package, posted to
- comp.sources.misc, volume 4. It does require a installing a
- device driver, but works flawlessly once installed. (Its
- resolution is limited to the kernel HZ value, since it uses the
- kernel delay() routine.)
-
- 4.7) How can I get setuid shell scripts to work?
-
- [ This is a long answer, but it's a complicated and frequently-asked
- question. Thanks to Maarten Litmaath for this answer, and
- for the "indir" program mentioned below. ]
-
- Let us first assume you are on a UNIX variant (e.g. 4.3BSD or
- SunOS) that knows about so-called `executable shell scripts'.
- Such a script must start with a line like:
-
- #!/bin/sh
-
- The script is called `executable' because just like a real (binary)
- executable it starts with a so-called `magic number' indicating
- the type of the executable. In our case this number is `#!' and
- the OS takes the rest of the first line as the interpreter for
- the script, possibly followed by 1 initial option like:
-
- #!/bin/sed -f
-
- Suppose this script is called `foo' and is found in /bin,
- then if you type:
-
- foo arg1 arg2 arg3
-
- the OS will rearrange things as though you had typed:
-
- /bin/sed -f /bin/foo arg1 arg2 arg3
-
- There is one difference though: if the setuid permission bit for
- `foo' is set, it will be honored in the first form of the
- command; if you really type the second form, the OS will honor
- the permission bits of /bin/sed, which is not setuid, of course.
-
- ----------
-
- OK, but what if my shell script does NOT start with such a `#!'
- line or my OS does not know about it?
-
- Well, if the shell (or anybody else) tries to execute it, the OS
- will return an error indication, as the file does not start with
- a valid magic number. Upon receiving this indication the shell
- ASSUMES the file to be a shell script and gives it another try:
-
- /bin/sh shell_script arguments
-
- But we have already seen that a setuid bit on `shell_script' will
- NOT be honored in this case!
-
- ----------
-
- Right, but what about the security risks of setuid shell scripts?
-
- Well, suppose the script is called `/etc/setuid_script', starting
- with:
-
- #!/bin/sh
-
- Now let us see what happens if we issue the following commands:
-
- $ cd /tmp
- $ ln /etc/setuid_script -i
- $ PATH=.
- $ -i
-
- We know the last command will be rearranged to:
-
- /bin/sh -i
-
- But this command will give us an interactive shell, setuid to the
- owner of the script!
- Fortunately this security hole can easily be closed by making the
- first line:
-
- #!/bin/sh -
-
- The `-' signals the end of the option list: the next argument `-i'
- will be taken as the name of the file to read commands from, just
- like it should!
-
- ---------
-
- There are more serious problems though:
-
- $ cd /tmp
- $ ln /etc/setuid_script temp
- $ nice -20 temp &
- $ mv my_script temp
-
- The third command will be rearranged to:
-
- nice -20 /bin/sh - temp
-
- As this command runs so slowly, the fourth command might be able
- to replace the original `temp' with `my_script' BEFORE `temp' is
- opened by the shell! There are 4 ways to fix this security hole:
-
- 1) let the OS start setuid scripts in a different, secure way
- - System V R4 and 4.4BSD use the /dev/fd driver to pass the
- interpreter a file descriptor for the script
-
- 2) let the script be interpreted indirectly, through a frontend
- that makes sure everything is all right before starting the
- real interpreter - if you use the `indir' program from
- comp.sources.unix the setuid script will look like this:
-
- #!/bin/indir -u
- #?/bin/sh /etc/setuid_script
-
- 3) make a `binary wrapper': a real executable that is setuid and
- whose only task is to execute the interpreter with the name of
- the script as an argument
-
- 4) make a general `setuid script server' that tries to locate the
- requested `service' in a database of valid scripts and upon
- success will start the right interpreter with the right
- arguments.
-
- ---------
-
- Now that we have made sure the right file gets interpreted, are
- there any risks left?
-
- Certainly! For shell scripts you must not forget to set the PATH
- variable to a safe path explicitly. Can you figure out why?
- Also there is the IFS variable that might cause trouble if not
- set properly. Other environment variables might turn out to
- compromise security as well, e.g. SHELL... Furthermore you must
- make sure the commands in the script do not allow interactive
- shell escapes! Then there is the umask which may have been set
- to something strange...
-
- Etcetera. You should realise that a setuid script `inherits' all
- the bugs and security risks of the commands that it calls!
-
- All in all we get the impression setuid shell scripts are quite a
- risky business! You may be better off writing a C program instead!
-
- 4.8) How can I find out which user or process has a file open or is using
- a particular file system (so that I can unmount it?)
-
- Use fuser (system V), fstat (BSD), ofiles (public domain) or
- pff (public domain). These programs will tell you various things
- about processes using particular files.
-
- A port of the 4.3 BSD fstat to Dynix, SunOS and Ultrix
- can be found in archives of comp.sources.unix, volume 18.
-
- pff is part of the kstuff package, and works on quite a few systems.
- Instructions for obtaining kstuff are provided in question 3.10.
-
- 4.9) How do I keep track of people who are fingering me?
-
- From: jik@pit-manager.MIT.EDU (Jonathan I. Kamens)
- From: malenovi@plains.NoDak.edu (Nikola Malenovic)
- Date: Mon, 23 Nov 1992 16:01:45 -0600
-
- Generally, you can't find out the userid of someone who is
- fingering you from a remote machine. You may be able to
- find out which machine the remote request is coming from.
- One possibility, if your system supports it and assuming
- the finger daemon doesn't object, is to make your .plan file a
- "named pipe" instead of a plain file. (Use 'mknod' to do this.)
-